/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.volley;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import net.apexes.commons.lang.Checks;

/**
 * 
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 *
 * @param <Q> Request数据类型
 * @param <R> Response数据类型
 */
public class GsonRequest<Q, R> extends Request<R> {
    
    public static final String PROTOCOL_CHARSET = "utf-8";
    
    public static final String PROTOCOL_CONTENT_TYPE = "application/json; charset=" + PROTOCOL_CHARSET;
        
    private final Q mRequestObject;
    private final Type mResponseType;
    private final Listener<R> mListener;
    private final Gson mGson;
    private ResponseProcessor<R> mProcessor;
    
    /**
     * 
     * @param url
     * @param requestObject 要POST的数据
     * @param responseType Response数据要转成的对象类型
     * @param listener
     * @param errorListener
     */
    public GsonRequest(String url, Q requestObject, Type responseType,
            Listener<R> listener, ErrorListener errorListener) {
        this(url, requestObject, responseType, new Gson(), listener, errorListener);
    }
    
    /**
     * 
     * @param url
     * @param requestObject 要POST的数据
     * @param responseType Response数据要转成的对象类型
     * @param gson
     * @param listener
     * @param errorListener
     */
    public GsonRequest(String url, Q requestObject, Type responseType, Gson gson,
            Listener<R> listener, ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        Checks.verifyNotNull(gson, "gson");
        Checks.verifyNotNull(responseType, "responseType");
        Checks.verifyNotNull(listener, "listener");
        this.mGson = gson;
        this.mRequestObject = requestObject;
        this.mResponseType = responseType;
        this.mListener = listener;
    }
    
    @Override
    public String getBodyContentType() {
        return PROTOCOL_CONTENT_TYPE;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        try {
            String body = mGson.toJson(mRequestObject);
            return body.getBytes(PROTOCOL_CHARSET);
        } catch (UnsupportedEncodingException uee) {
            return null;
        }
    }

    @Override
    protected void deliverResponse(R response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<R> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            R responseValue = toResponseValue(json, mResponseType);
            return Response.success(responseValue, HttpHeaderParser.parseCacheHeaders(response));
        } catch (Exception e) {
            return Response.error(new ParseError(e));
        }
    }
    
    protected R toResponseValue(String json, Type responseType) throws Exception {
        R value = mGson.fromJson(json, responseType);
        if (mProcessor != null) {
            mProcessor.process(value);
        }
        return value;
    }
    
    public void setResponseProcessor(ResponseProcessor<R> processor) {
        this.mProcessor = processor;
    }
    
    /**
     * Response处理器。在从网络获取到数据并转换成Response对象后将使用此处理器先进行处理
     * 
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     * @param <R>
     */
    public static interface ResponseProcessor<R> {
        
        /**
         * 对response进行处理
         * @param response
         * @return
         */
        R process(R response);
    }

}
